package com.stream;

import com.google.common.collect.Lists;
import lombok.Builder;
import lombok.Data;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Clock;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.*;

/**
 * @Author:zxh
 * @Description: java.util.stream.Collectors中的方法熟悉
 * @Date: 2020/11/4
 * @Modified By:
 */
public class CollectorsTest {
    /**
     * 参考：https://www.matools.com/api/java8
     * https://blog.csdn.net/u013291394/article/details/52662761
     * https://github.com/CL0610/Java8-learning
     */

    /**
     * 1.将数据流缩减为一个单一值：一个流执行后的结果能够被缩减为一个单一的值。单一的值可以是一个Collection，或者像int、double等的数值，再或者是一个用户自定义的值对象
     * 2.将一个数据流中的元素进行分组：根据任务类型将流中所有的任务进行分组。这将产生一个Map<TaskType, List<Task>>的结果，其中每个实体包含一个任务类型以及与它相关的任务。
     * 你也可以使用除了列表以外的任何其他的集合。如果你不需要与一任务类型相关的所有的任务，你可以选择产生一个Map<TaskType, Task>。
     * 这是一个能够根据任务类型对任务进行分类并获取每类任务中第一个任务的例子。
     * 3.分割一个流中的元素：你可以将一个流分割为两组——比如将任务分割为要做和已经做完的任务
     */

    /**
     * 根据任务类型来对任务进行分类的例子
     */
    public Map<String, List<Task>> groupTaskByType(List<Task> taskList) throws IOException {
        taskList.stream().map(task -> task.getTitle()).collect(toList());
        taskList.stream().map(task -> task.getTitle()).collect(toSet());
        //使用toMap收集器将一个流转换为一个映射;task -> task是一个用来返回任务本身的lambda表达式
        Map<String, Task> collect = taskList.stream().collect(Collectors.toMap(Task::getTitle, task -> task));
        //使用Function接口中的默认方法identity来改进上面展示的代码
        Map<String, Task> collect1 = taskList.stream().collect(toMap(Task::getTitle, Function.identity()));
        //使用toMap方法的另一个变体来处理重复问题，它允许我们指定一个合并方法。这个合并方法允许用户他们指定想如何处理多个值关联到同一个键的冲突
        LinkedHashMap<String, Task> collect2 = taskList.stream().collect(toMap(Task::getTitle, Function.identity(), (t1, t2) -> t2, LinkedHashMap::new));
        //当你想要将结果收集到其它类型的集合中时，你可以像下面这样使用toCollection收集器。
        LinkedHashSet<Task> collect3 = taskList.stream().collect(toCollection(LinkedHashSet::new));
        //找到拥有最长标题的任务
        Optional<Task> collect4 = taskList.stream().collect(collectingAndThen(maxBy((t1, t2) -> t1.getTitle().length() - t2.getTitle().length()), Function.identity()));
        //统计标签的总数
        Integer collect5 = taskList.stream().collect(summingInt(task -> task.getTag().length()));
        //生成任务标题的概述
        String collect6 = taskList.stream().map(task -> task.getTitle()).collect(joining(";"));

        /**
         * 分类收集器
         */
        //根据TaskType来对所有的任务进行分类
        Map<String, List<Task>> collect7 = taskList.stream().collect(groupingBy(Task::getType));
        //根据标签分类
        //taskList.stream().flatMap(task -> task.getTaskList().stream().map(task1 -> new Task(task1,"")))
        Map<String, List<Task>> collect8 = taskList.stream().collect(groupingBy(Task::getTag, mapping(Task::getTask, toList())));
        //根据标签和数量对任务分类
        Map<String, Long> collect9 = taskList.stream().collect(groupingBy(Task::getTag, counting()));
        //根据任务类型和创建日期分类
        Map<String, Map<String, List<Task>>> collect10 = taskList.stream().collect(groupingBy(Task::getType, groupingBy(Task::getCreateTime)));

        /**
         * 分割，很多时候你想根据一个断言来将一个数据集分割成两个数据集
         */
        Map<Boolean, List<Task>> collect11 = taskList.stream().collect(partitioningBy(task -> task.getDueOne().toInstant().isAfter(Clock.systemUTC().instant())));
        IntSummaryStatistics collect12 = taskList.stream().map(Task::getTitle).collect(summarizingInt(String::length));
        collect12.getCount();
        collect12.getMax();
        collect12.getMin();
        collect12.getSum();
        System.out.println(collect12.getAverage());

        //使用流和收集器在Java8中编写有名的字数统计样例
        Path path = null;
        Map<Object, Long> collect13 = Files.lines(path)
                .parallel()
                .flatMap(line -> Arrays.stream(line.trim().split("\\s")))
                .map(word -> word.replaceAll("[^a-zA-Z]", "").toLowerCase().trim())
                .filter(word -> word.length() > 0)
                .map(word -> new AbstractMap.SimpleEntry(word, 1))
                .collect(groupingBy(AbstractMap.SimpleEntry::getKey, counting()));

        collect13.forEach((k, v) -> System.out.println(String.format("%s ==>> %d", k, v)));

        return taskList.stream().collect(Collectors.groupingBy(task -> task.getType()));
    }

    //使用java7进行分组的代码
    public void java7GroupBy() {
        List<Task> taskList = new Task().getTaskList();
        Map<String, List<Task>> hasMap = new HashMap<>();
        for (Task task : taskList) {
            List<Task> tasks = hasMap.get(task.getType());
            if (tasks == null) {
                List<Task> tasksType = new ArrayList<>();
                tasksType.add(task);
                hasMap.put(task.getType(), tasksType);
            } else {
                tasks.add(task);
            }
        }

        for (Map.Entry<String, List<Task>> entry : hasMap.entrySet()
                ) {
            System.out.println("" + entry.getKey() + " " + entry.getValue());

        }
    }

    class Task {
        private String type;
        private String title;
        private String tag;
        private String createTime;
        private Date dueOne;

        public Date getDueOne() {
            return dueOne;
        }

        private List<Task> task = null;

        public String getType() {
            return type;
        }

        public String getTitle() {
            return title;
        }

        public String getTag() {
            return tag;
        }

        public String getCreateTime() {
            return createTime;
        }

        public Task getTask() {
            return new Task();
        }

        public List<Task> getTaskList() {
            return task = new ArrayList<>();
        }

    }


    /**
     * 实施方式中Collector实现各种有用的还原操作，如累加元件到集合，根据各种标准总结元件等
     */
    public void toolsCollectors() {
        ArrayList<People> people = new ArrayList<>();
        //Accumulate names into a List
        List<System> collect = people.stream().map(People::getName).collect(toList());
        //Accumulate names into a TreeSet
        TreeSet<System> collect1 = people.stream().map(People::getName).collect(Collectors.toCollection(TreeSet::new));

        //将元素转换为字符串并连接它们，用逗号分隔
        String collect2 = people.stream().map(Object::toString).collect(Collectors.joining(", "));
        //计算员工工资总额
        people = new ArrayList<>();
        //int collect3 = people.stream().collect(Collectors.summingInt(People::getEmloyees));
        // Group employees by department
        List<Employee> employees = new ArrayList<>();
        //Map<String, List<Employee>> collect4 = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));

        // 按部门计算工资总额
        employees = null;
        employees.stream().collect(Collectors.groupingBy(t -> t.getDepartment(), Collectors.summingInt(em -> em.getSalary())));
        // Partition students into passing and failing
        // Map<Boolean, List<Student>> passingFailing = students.stream() .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));


    }

    class People {
        private System name;
        private String emloyees;

        public String getEmloyees() {
            return emloyees;
        }

        public System getName() {
            return name;
        }
    }

    class Employee {
        private Integer salary;
        private String department;

        public Integer getSalary() {
            return salary;
        }

        public String getDepartment() {
            return department;
        }
    }

}

/**
 * collectors不常见方法测试，(参考源码的demo)
 * 常见方法见collectors的方法--见名思意
 */
class JdkApiCollectorsTest {

    public static void main(String[] args) {
        /**
         * 1.Stream.collect(Collectors)中，Collectors的方法测试
         */
        ArrayList<Person> personArrayList = Lists.newArrayList();
        personArrayList.add(Person.builder().state("纽约州").city("纽约市").num(12).build());
        personArrayList.add(Person.builder().state("亚特兰州").city("亚特兰市").num(13).build());
        personArrayList.add(Person.builder().state("底特律州").city("底特律市").num(14).build());
        personArrayList.add(Person.builder().state("底特律州").city("底特律市").num(19).build());
        personArrayList.add(Person.builder().state("底特律州").city("底特律市2").num(16).build());
        //1.1groupingBy分组
        Map<String, List<Person>> collect = personArrayList.stream().collect(groupingBy(Person::getCity));
        //Map<String, Map<String, List<Person>>> collect1 = personArrayList.stream().collect(groupingBy(Person::getState,toList()));
        //1.2mapping:通过在累积之前对每个输入元素应用映射函数，将一个接受U类型元素的Collector调整为一个接受T类型元素的Collector
        Map<String, Set<String>> collect4 = personArrayList.stream().collect(groupingBy(Person::getState, mapping(Person::getCity, toSet())));
        //1.3reducing:返回 Collector执行下一个指定的减少其输入元件的 BinaryOperator使用所提供的身份。
        Comparator<Person> comparing = Comparator.comparing(Person::getNum);
        Map<String, Optional<Person>> collect5 = personArrayList.stream().collect(groupingBy(Person::getState, reducing(BinaryOperator.maxBy(comparing))));
        System.out.println("断点");
        //1.4collect方法初步认识的demo
        List<String> strings = Arrays.asList("a", "b", "c", "d", "e", "f");
        ArrayList<String> collect2 = strings.stream().collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
        String collect3 = strings.stream().collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString();
        System.out.println("断点");
    }

}

/**
 * 测试使用
 */
@Builder
@Data
class Person {
    private String state;
    private String city;
    private Integer num;
}